package com.ming.core.parser.graph;

import com.ming.core.parser.entity.FlowData;
import com.ming.core.parser.entity.edge.Edge;
import com.ming.core.parser.entity.node.Node;
import com.yomahub.liteflow.builder.el.ELWrapper;
import lombok.Data;

import java.util.*;
import java.util.stream.Collectors;

@Data
public class Graph {
    // 邻接表
    Map<String, List<String>> adjList = new LinkedHashMap<>();
    // 反向邻接表
    Map<String, List<String>> reverseAdjList = new LinkedHashMap<>();
    // 节点列表
    Map<String, Node> nodes = new LinkedHashMap<>();
    // source节点集合
    Set<String> sources = new LinkedHashSet<>();
    // target节点集合
    Set<String> targets = new LinkedHashSet<>();
    // 即在source中也在target中的节点
    Set<String> sourceTargets = new LinkedHashSet<>();
    // 即不在source中也不在target中的节点
    Set<String> singleNodes = new LinkedHashSet<>();

    // 起始节点
    List<Node> startNodes;
    // 结束节点
    List<Node> endNodes;
    // 所有路径
    List<List<Node>> allPaths;
    // 并行分组不相交路径
    List<List<List<Node>>> groupPaths;
    // 分段节点
    //List<Node> segmentationPoints;

    // 流程数据
    private FlowData data;

    public Graph() { }

    public Graph(FlowData data) {
        this.data = data;
        init();
    }

    public void init(){
        data.getNodes().forEach(this::addNode);
        data.getEdges().forEach(this::addEdge);
        this.sources = data.getEdges().stream().map(Edge::getSource).collect(Collectors.toSet());
        this.targets = data.getEdges().stream().map(Edge::getTarget).collect(Collectors.toSet());
        this.sourceTargets = data.getEdges().stream().map(Edge::getSource).filter(data.getEdges().stream().map(Edge::getTarget).collect(Collectors.toSet())::contains).collect(Collectors.toSet());
        this.singleNodes = data.getNodes().stream().filter(this::isSingle).map(Node::getId).collect(Collectors.toSet());
        this.startNodes = getStartNodes();
        this.endNodes = getEndNodes();
        this.allPaths = findAllPaths();//所有路径
        this.groupPaths = groupPathsByIntersection(this.allPaths);//分组路径
//        List<Node> segmentationPoints = findSegmentationPoints(groupPaths.get(0));//分段节点
//        List<List<Node>> processSegments = getProcessSegments(segmentationPoints);
        System.out.println();
    }

    //添加节点
    public void addNode(Node node) {
        adjList.putIfAbsent(node.getId(), new ArrayList<>());
        reverseAdjList.putIfAbsent(node.getId(), new ArrayList<>());
        nodes.put(node.getId(), node);
    }

    //添加路径
    public void addEdge(Edge edge) {
        adjList.get(edge.getSource()).add(edge.getTarget());
        reverseAdjList.get(edge.getTarget()).add(edge.getSource());
    }

    //判断一个节点是否是单节点，即节点在data.getEdges的source和target中都没有出现
    public boolean isSingle(Node node){
        return GraphUtil.isSingle(node,data.getEdges());
    }

    public List<Node> getNeighbors(Node node) {
        List<Node> neighbors = new ArrayList<>();
        List<String> neighborIds = adjList.get(node.getId()); // 获取邻接ID列表
        if (neighborIds != null) {
            for (String id : neighborIds) {
                neighbors.add(nodes.get(id)); // 通过ID获取节点
            }
        }
        return neighbors;
    }

    public boolean isNeighbor(Node node1, Node node2) {
        List<String> neighbors = adjList.get(node1.getId()); // 获取第一个节点的邻接ID列表
        return neighbors != null && neighbors.contains(node2.getId()); // 检查第二个节点的ID是否在列表中
    }

    public List<Node> getPrev(Node node) {
        List<Node> neighbors = new ArrayList<>();
        List<String> neighborIds = reverseAdjList.get(node.getId()); // 获取反向邻接ID列表
        if (neighborIds != null) {
            for (String id : neighborIds) {
                neighbors.add(nodes.get(id)); // 通过ID获取节点
            }
        }
        return neighbors;
    }

    //获取开始节点集合，根据sourcs和targets
    public List<Node> getStartNodes() {
        return GraphUtil.getStartNodes(targets,data.getNodes());
    }

    //获取开始结束集合，根据sourcs和targets
    public List<Node> getEndNodes() {
        return GraphUtil.getEndNodes(sources,data.getNodes());
    }

    // 判断allPaths是否存在相交点，并将有交点的路径分为一组，返回List<List<List<Node>>>
    // 判断路径是否存在交点，并按交点分组
    public List<List<List<Node>>> groupPathsByIntersection(List<List<Node>> allPaths) {
        return GraphUtil.groupPathsByIntersection(allPaths);
    }

    //找到所有路径的方法
    public List<List<Node>> findAllPaths() {
        List<List<Node>> allPaths = new ArrayList<>();
        List<Node> startNodes = getStartNodes();
        for (Node startNode : startNodes) {
            List<Node> visited = new ArrayList<>();
            GraphUtil.findAllPathsHelper(startNode, visited, allPaths, adjList, nodes);
        }
        return allPaths;
    }

    // 找到所有路径的方法,根据起始节点和结束节点
    public List<List<Node>> findAllPaths(String startId, String endId) {
        List<List<Node>> paths = new ArrayList<>();
        Set<String> visited = new HashSet<>();
        GraphUtil.findPathsRecursive(startId, endId, new ArrayList<>(), paths, visited, adjList, nodes);
        return paths;
    }

    //获取并行分段集合
    /*public List<List<List<Node>>> getProcessSegments() {
        if(startNodes.size() == 1 && endNodes.size() == 1){
            return new ArrayList<List<List<Node>>>(){{add(getProcessSegments(startNodes.get(0).getId(), endNodes.get(0).getId()));}};
        }else if(startNodes.size() == 1 && endNodes.size() == 0){
            return new ArrayList<List<List<Node>>>(){{add(getProcessSegments(startNodes.get(0).getId(), null));}};
        }else if(startNodes.size() > 1){
            Node commonNode = findCommonNode(startNodes);
            List<List<List<Node>>> lists = new ArrayList<>();
            if(commonNode == null){
                for (Node node : startNodes){
                    lists.add(getProcessSegments(node.getId(), null));
                }
            }else{
                lists.add(getProcessSegments(commonNode.getId(), null));
                throw new RuntimeException("流程存在多起点并且节点相交！");
            }
            return lists;
        }
        throw new RuntimeException("流程存在多起点、多结束点！");
    }*/


    //获取分段集合
    public List<List<Node>> getProcessSegments(String startId, String endId) {
        List<Node> segmentationPoints = GraphUtil.findSegmentationPoints(findAllPaths(startId, endId), nodes);
        Set<String> segmentPointsSet = new HashSet<>();
        for (Node node : segmentationPoints) {
            segmentPointsSet.add(node.id);
        }
        segmentPointsSet.add(endId); // 确保终点也被视为分段点

        Map<String, Node> visited = new HashMap<>();
        List<List<Node>> segments = new ArrayList<>();
        Queue<Node> queue = new LinkedList<>();
        queue.offer(nodes.get(startId));

        List<Node> currentSegment = new ArrayList<>();
        boolean isSegmentStart = true;

        while (!queue.isEmpty()) {
            Node currentNode = queue.poll();

            if (visited.containsKey(currentNode.id)) {
                continue;
            }
            visited.put(currentNode.id, currentNode);

            if (segmentPointsSet.contains(currentNode.id)) {
                if (!currentSegment.isEmpty()) {
                    segments.add(new ArrayList<>(currentSegment));
                    currentSegment.clear();
                }
                // 分段点单独成段
                segments.add(Collections.singletonList(currentNode));
                isSegmentStart = true;
            } else {
                if (isSegmentStart) {
                    isSegmentStart = false;
                    currentSegment = new ArrayList<>();
                }
                currentSegment.add(currentNode);
            }

            List<String> neighbors = adjList.get(currentNode.id);
            if (neighbors != null) {
                for (String neighbor : neighbors) {
                    if (!visited.containsKey(neighbor)) {
                        queue.offer(nodes.get(neighbor));
                    }
                }
            }
        }

        // 添加最后一个非空段
        if (!currentSegment.isEmpty()) {
            segments.add(currentSegment);
        }

        return segments;
    }

    public ELWrapper toEL(){
        return GraphToEL.toEL(this);
    }

    /**
     * 通过起始节点集合获取相交节点
     * @param startNodes
     * @return
     */
    public Node findJoinNode(List<Node> startNodes) {
        if (startNodes == null || startNodes.isEmpty()) {
            return null;
        }

        // 创建一个用于存储每个节点到达的所有节点的map
        Map<String, Set<String>> allPaths = new HashMap<>();

        // 对每个起始节点进行深度优先搜索，记录路径
        for (Node startNode : startNodes) {
            Set<String> visited = new HashSet<>();
            GraphUtil.dfs(startNode.getId(), visited, adjList);
            allPaths.put(startNode.getId(), visited);
        }

        // 找到所有路径中共有的节点
        Set<String> commonNodes = new HashSet<>(allPaths.values().iterator().next());
        for (Set<String> path : allPaths.values()) {
            commonNodes.retainAll(path);
        }

        // 返回第一个公共节点
        for (String nodeId : commonNodes) {
            return nodes.get(nodeId);
        }

        return null;
    }


}
